package io.github.jsp.cluster;

import io.github.jsp.model.JobRegistry;
import io.github.jsp.service.JobRegistryService;
import io.github.jsp.util.ServerIdUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@Component
public class ClusterManager {
    
    private static final Logger logger = LoggerFactory.getLogger(ClusterManager.class);
    
    @Autowired
    private JobRegistryService jobRegistryService;
    
    private ScheduledExecutorService scheduler;
    
    private static final String REGISTRY_GROUP = "EXECUTOR";
    private static final long HEARTBEAT_INTERVAL = 30; // seconds
    private static final long NODE_TIMEOUT = 90; // seconds
    
    @PostConstruct
    public void start() {
        scheduler = Executors.newScheduledThreadPool(2, r -> {
            Thread t = new Thread(r, "ClusterManager");
            t.setDaemon(true);
            return t;
        });
        
        registerCurrentNode();
        
        scheduler.scheduleAtFixedRate(this::heartbeat, 
            HEARTBEAT_INTERVAL, HEARTBEAT_INTERVAL, TimeUnit.SECONDS);
        
        scheduler.scheduleAtFixedRate(this::cleanExpiredNodes, 
            60, 60, TimeUnit.SECONDS);
        
        logger.info("ClusterManager started");
    }
    
    @PreDestroy
    public void stop() {
        try {
            unregisterCurrentNode();
            
            if (scheduler != null) {
                scheduler.shutdown();
                if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {
                    scheduler.shutdownNow();
                }
            }
            
            logger.info("ClusterManager stopped");
        } catch (Exception e) {
            logger.error("Error stopping ClusterManager", e);
        }
    }
    
    private void registerCurrentNode() {
        try {
            String nodeId = ServerIdUtil.getNodeId();
            String serverAddress = ServerIdUtil.getServerAddress();
            
            JobRegistry registry = new JobRegistry();
            registry.setRegistryGroup(REGISTRY_GROUP);
            registry.setRegistryKey(nodeId);
            registry.setRegistryValue(serverAddress);
            
            jobRegistryService.registry(registry);
            
            logger.info("Node registered: nodeId={}, address={}", nodeId, serverAddress);
        } catch (Exception e) {
            logger.error("Error registering current node", e);
        }
    }
    
    private void unregisterCurrentNode() {
        try {
            String nodeId = ServerIdUtil.getNodeId();
            jobRegistryService.removeDead(REGISTRY_GROUP, nodeId);
            
            logger.info("Node unregistered: nodeId={}", nodeId);
        } catch (Exception e) {
            logger.error("Error unregistering current node", e);
        }
    }
    
    private void heartbeat() {
        try {
            registerCurrentNode(); // 更新注册信息相当于心跳
        } catch (Exception e) {
            logger.error("Error sending heartbeat", e);
        }
    }
    
    private void cleanExpiredNodes() {
        try {
            long expireTime = System.currentTimeMillis() - NODE_TIMEOUT * 1000;
            jobRegistryService.removeDeadByTime(expireTime);
            
        } catch (Exception e) {
            logger.error("Error cleaning expired nodes", e);
        }
    }
    
    public List<JobRegistry> getActiveNodes() {
        try {
            return jobRegistryService.findAll(REGISTRY_GROUP);
        } catch (Exception e) {
            logger.error("Error getting active nodes", e);
            return null;
        }
    }
    
    public int getActiveNodeCount() {
        List<JobRegistry> nodes = getActiveNodes();
        return nodes != null ? nodes.size() : 0;
    }
    
    public boolean isCurrentNodeActive() {
        try {
            String nodeId = ServerIdUtil.getNodeId();
            JobRegistry registry = jobRegistryService.findByKey(REGISTRY_GROUP, nodeId);
            
            if (registry == null) {
                return false;
            }
            
            long updateTime = registry.getUpdateTime().getTime();
            long currentTime = System.currentTimeMillis();
            
            return (currentTime - updateTime) < NODE_TIMEOUT * 1000;
            
        } catch (Exception e) {
            logger.error("Error checking current node status", e);
            return false;
        }
    }
}